using System;
using System.IO;
//using System.Text;
using Microsoft.DirectX;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Noein.Control
{
    public class KeyframeController : Controller
    {
        // TODO instead of u, use t -> u mapping
        private float u = 0;
        private int previousKey = 0;
        private Interpolator.Interpolate interpolate;
        public Interpolator.Interpolate Interpolate
        {
            get { return interpolate; }
            set { interpolate = value; }
        }

        //private struct Key
        //{
        //    public float time;
        //    public float x, y, z;       // position
        //    public float xa, ya, za;    // rotation axis
        //    public float angle;         // rotation angle
        //}
        //private List<Key> keyframes = new List<Key>();
        #region Keyframe
        private List<float> time = new List<float>();
        private List<float> x = new List<float>();
        private List<float> y = new List<float>();
        private List<float> z = new List<float>();
        private List<float> xa = new List<float>();
        private List<float> ya = new List<float>();
        private List<float> za = new List<float>();
        private List<float> angle = new List<float>();
        #endregion

        public KeyframeController(string srcFile)
        {
            ReadFile(srcFile);
            interpolate = Interpolator.Interpolator.Linear;
        }

        public override void Reset(Object obj)
        {
            base.Reset(obj);
            obj.Spatial.MoveTo(new Vector3(x[0], y[0], z[0]));
            Quaternion orientation = new Quaternion();
            orientation.RotateAxis(new Vector3(xa[0], ya[0], za[0]), angle[0]);
            obj.Spatial.Orientation = orientation;

            u = 0;
            previousKey = 0;
        }

        public override void Update(Object obj, float dt)
        {
            if (Pause)
                return;

            // update key frame animation time and find the previous key
            u += dt;
            while (time[previousKey + 1] < u)
            {
                previousKey++;

                // on passing the last key
                if ((previousKey + 1) == time.Count)
                {
                    if (Loop)
                    {
                        // loop back to the start
                        previousKey = 0;
                        u %= time[time.Count - 1];
                    }
                    else
                    {
                        // reset to start and pause
                        Reset(obj);
                        return;
                    }
                }
            }

            int nextKey = previousKey + 1;

            float normalizedTime = (u - time[previousKey]) / (time[nextKey] - time[previousKey]);

            obj.Spatial.MoveTo(new Vector3(
                interpolate(x, previousKey, normalizedTime),
                interpolate(y, previousKey, normalizedTime),
                interpolate(z, previousKey, normalizedTime)));

            obj.Spatial.Orientation = Quaternion.Slerp(
                Quaternion.RotationAxis(new Vector3(xa[previousKey], ya[previousKey], za[previousKey]), angle[previousKey]),
                Quaternion.RotationAxis(new Vector3(xa[nextKey], ya[nextKey], za[nextKey]), angle[nextKey]),
                normalizedTime);
        }

        public override void AttachTo(Object obj)
        {
            //throw new System.Exception("The method or operation is not implemented.");
            if (obj.Spatial == null)
                obj.Spatial = new Spatial();

            if (obj.Visual == null)
                throw new Exception("Keyframe animation needs visual!");

            obj.Controllers.Add(this);

            Reset(obj);
        }

        private void ReadFile(string srcFile)
        {
            if (!File.Exists(@"..\..\" + srcFile))
            {
                throw new Exception("Key frame input file not found!");
            }

            using (StreamReader sr = File.OpenText(@"..\..\" + srcFile))
            {
                time.Clear();
                x.Clear();
                y.Clear();
                z.Clear();
                xa.Clear();
                ya.Clear();
                za.Clear();
                angle.Clear();

                string line = sr.ReadLine();
                if (line == null)
                    throw new Exception("Key frame input file is empty!");

                do
                {
                    // split each line considering space(s) as separator
                    Regex o = new Regex(@"[\s]+");
                    string[] entries = o.Split(line);

                    // add a keyframe
                    time.Add((float)Convert.ToDouble(entries[0]));
                    x.Add((float)Convert.ToDouble(entries[1]));
                    y.Add((float)Convert.ToDouble(entries[2]));
                    z.Add((float)Convert.ToDouble(entries[3]));
                    xa.Add((float)Convert.ToDouble(entries[4]));
                    ya.Add((float)Convert.ToDouble(entries[5]));
                    za.Add((float)Convert.ToDouble(entries[6]));
                    angle.Add((float)Convert.ToDouble(entries[7]));
                } while ((line = sr.ReadLine()) != null);
                sr.Close();
            }
        }

    }
}
